/*	Classes used for communication with the client.
	
	Created by Cronos
	Credits:
		GWCA Project for the base to build on
		Google for many nice codesnippets to use
		Gamedeception for the hooking tutorial*/

#ifndef COMMUNICATOR_H
#define COMMUNICATOR_H

#include "ConcurrentQueue.h"
#include "CompletitionPort.h"

#define BUFSIZE 1024
#define TEXTBUFFERLENGTH 504

union Param_t{
	int 	i;
	float 	f;
	DWORD	d;

	Param_t(){memset(this,0,sizeof(Param_t));}
};

enum message_type
{
	IS_NUMERIC = 0x00,
	IS_TEXT = 0x01,
	IS_COMMAND = 0x02,
	IS_REQUEST = 0x04,
	IS_RESPONSE = 0x08
};

enum PIPE_COMMANDS{
	//Pipe Control
	CA_DisconnectPipe = 0x001, CA_AliveRequest, CA_IsAlive,

	//Error Control
	CA_Error = 0x050,

	//Commands
	CA_CommandsBegin = 0x100,
	CA_TestCommand, CA_SetSpeed, CA_MoveTo, CA_UseItem, CA_SelectTarget, CA_UseSkill,
	CA_LootItem, CA_SummonPet, CA_TalkToNpc, CA_QuestAccept, CA_QuestReward, CA_DestroyItem,
	CA_RepairItems, CA_ChangeRealm, CA_UseItemByItemId, CA_FollowPlayer, CA_Interact,
	CA_AutoLootOnOff,
	CA_CommandsEnd,

	//Requests
	CA_RequestsBegin = 0x300,
	CA_TestRequest, CA_GetCurrentTargetId, CA_PlayerIterFirst, CA_PlayerIterNext, CA_GetItemIdByIndex,
	CA_GetCoords, CA_GetDistFromPlayerToPlayer, CA_GetPlayerType, CA_GetPlayerName, CA_GetMyId,
	CA_GetHp, CA_GetPetPlayerId, CA_GetSelectedQuestId, CA_GetPetHp, CA_GetPlayerIdByName,
	CA_GetServerRealmInfo, CA_GetPartySize, CA_GetPartyMemberInfo, CA_GetClosestPlayerByName,
	CA_GetIsDead,
	CA_RequestsEnd
};

struct BaseMessage
{
	unsigned short header;
	unsigned short type;
	Param_t Param1, Param2, Param3, Param4, Param5;
	char buffer[100];

	BaseMessage() { memset(this, 0, sizeof(BaseMessage)); }
};

typedef struct 
{ 
	OVERLAPPED oOverlap;
	HANDLE hPipeInst;
	BaseMessage bmRequest;
	DWORD cbRead;
	BaseMessage bmReply; 
	DWORD cbWritten;
} PIPEINST, *LPPIPEINST;

typedef void (*HandleMessageFunctionPointer)(BaseMessage);

class PipeServer
{
public:
	PipeServer(){
		sprintf_s(m_cPipeName, 30, "\\\\.\\pipe\\boy_%lu", GetCurrentProcessId());

		m_bEngineHook = false;
		m_HandleRequestsFunction = NULL;

		CreateThread(0, 0, PipeServer::TListenConnections, this, 0, 0);
		CreateThread(0, 0, PipeServer::TListenConnectedPipes, this, 0, 0);
	}
	~PipeServer(){
	};

	void AddCommand(BaseMessage& command)
	{
		m_CommandBuffer.push(command);
	}

	bool GetCommand()
	{
		if (m_CommandBuffer.try_pop(m_CurrCommand))
		{
			return true;
		}
		return false;
	}

	BaseMessage GetCommandMessage() {return m_CurrCommand;}
	WORD GetCommandHeader() {return m_CurrCommand.header;}
	Param_t GetCommandParam1(){return m_CurrCommand.Param1;}
	Param_t GetCommandParam2(){return m_CurrCommand.Param2;}
	Param_t GetCommandParam3(){return m_CurrCommand.Param3;}
	Param_t GetCommandParam4(){return m_CurrCommand.Param4;}
	Param_t GetCommandParam5(){return m_CurrCommand.Param5;}

	void SetRequestFunction(HandleMessageFunctionPointer pFunc)
	{
		m_HandleRequestsFunction = pFunc;
	}
	
	void SetResponse(WORD header, Param_t Param1 = Param_t(), Param_t Param2 = Param_t(),
		Param_t Param3 = Param_t(), Param_t Param4 = Param_t(), Param_t Param5 = Param_t())
	{
		SetResponseHeader(header);
		SetResponseParam1(Param1);
		SetResponseParam2(Param2);
		SetResponseParam3(Param3);
		SetResponseParam4(Param4);
		SetResponseParam5(Param5);
		SetResponseFlag(IS_NUMERIC | IS_RESPONSE);
	}

	void SetResponse(BaseMessage msg)
	{
		SetResponseMessage(msg);
		SetResponseFlag(IS_NUMERIC | IS_RESPONSE);
	}

	void ShowPipeName(){MessageBox(0, m_cPipeName, "PIPENAME", MB_OK); }

	unsigned long GetQueueSize() { return m_CommandBuffer.size(); }

private:
	void SetResponseMessage( BaseMessage msg ){m_CurrResponse = msg;}
	void SetResponseHeader( WORD header ){m_CurrResponse.header = header;}
	void SetResponseParam1( Param_t Param1 ){m_CurrResponse.Param1 = Param1;}
	void SetResponseParam2( Param_t Param2 ){m_CurrResponse.Param2 = Param2;}
	void SetResponseParam3( Param_t Param3 ){m_CurrResponse.Param3 = Param3;}
	void SetResponseParam4( Param_t Param4 ){m_CurrResponse.Param4 = Param4;}
	void SetResponseParam5( Param_t Param5 ){m_CurrResponse.Param5 = Param5;}
	void SetResponseFlag( int flag ){m_CurrResponse.type = flag;}

	bool ListenConnections();
	HRESULT ListenConnectedPipes();

	static DWORD WINAPI TListenConnections(PVOID thisPointer)
	{
		reinterpret_cast<PipeServer*>(thisPointer)->ListenConnections();

		return 0;
	}
	static DWORD WINAPI TListenConnectedPipes(PVOID thisPointer)
	{
		reinterpret_cast<PipeServer*>(thisPointer)->ListenConnectedPipes();

		return 0;
	}

	BaseMessage m_CurrCommand;
	BaseMessage m_CurrRequest;
	BaseMessage m_CurrResponse;

	bool m_bEngineHook;
	char m_cPipeName[30];
	HandleMessageFunctionPointer m_HandleRequestsFunction;

	CompletionPort m_Port;
	concurrent_queue<BaseMessage> m_CommandBuffer;
};

extern PipeServer* pipe;

#endif